package com.smh;

import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * @author shiminghui
 * @date 2025/3/11 16:03
 * @description: 实现一个词法分析器
 */
public class shiyan1 {

    private BufferedReader br; // 输入流
    private BufferedWriter bw; // 输出流
    private HashSet<String> keywords; // 关键字集合
    private HashMap<String, Identifier<Integer>> identifierMap; // 标识符集合
    private HashMap<Double, Integer> constants; // 常量集合
    private HashSet<String> operators; // 操作符集合
    private HashSet<Character> specialCharacters; // 特殊字符集合
    private final String inputFileName; // 输入文件名
    private final String outputFileName; // 输出文件名

    public shiyan1(String inputFileName, String outputFileName) {
        this.inputFileName = inputFileName;
        this.outputFileName = outputFileName;
    }

    public void start() {
        init();
        char c = getNextChar();
        int lineNumber = 1;
        int errorNumber = 0;
        while (c != (char) -1) {
            if (isLetter(c) || c == '_') { // 如果是字母和下划线开头
                StringBuilder sb = new StringBuilder();
                while (isLetter(c) || isDigit(c) || c == '_') {
                    sb.append(c);
                    c = getNextChar();
                    if (c == (char) -1) {
                        break;
                    }
                }
                String string = sb.toString();
                if (keywords.contains(string)) { // 关键字
                    writeOutput("(" + string + ",-)\n");
                } else { // 标识符
                    if (!identifierMap.containsKey(string)) { // 如果已经存在
                        int iSize = identifierMap.size();
                        writeOutput("(id," + iSize + ")\n");
                        identifierMap.put(string, new Identifier<>(string, "-", iSize));
                    } else {
                        Identifier<Integer> identifier = identifierMap.get(string);
                        writeOutput("(id," + identifier.value + ")\n");
                    }
                }
                continue;
            } else if (isSpaceOrTab(c)) {
                while (isSpaceOrTab(c)) {
                    c = getNextChar();
                }
                continue;
            } else if (isSpecialCharacter(c)) {
                writeOutput("(" + c + ",-)\n");
            } else if (isDigit(c)) {
                StringBuilder sb = new StringBuilder();
                while (isDigit(c)) {
                    sb.append(c);
                    c = getNextChar();
                    if (c == (char) -1) {
                        break;
                    }
                }
                if (c == '.') {
                    sb.append(c);
                    c = getNextChar();
                    while (isDigit(c)) {
                        sb.append(c);
                        c = getNextChar();
                        if (c == (char) -1) {
                            break;
                        }
                    }
                }
                double num = Double.parseDouble(sb.toString());
                if (!constants.containsKey(num)) {
                    int cSize = constants.size();
                    writeOutput("(num," + cSize + ")\n");
                    constants.put(num, cSize);
                }
                writeOutput("(num," + constants.get(num) + ")\n");
                continue;
            } else if (c == '\n' || c == '\r') {
                c = getNextChar();
                if (c == '\n') {
                    lineNumber++;
                    c = getNextChar();
                }
                continue;
            } else if (c == '/') { // 处理注释
                char nextChar = getNextChar();
                if (nextChar == '/') {
                    while ((c = getNextChar()) != '\n') {
                        // 忽略单行注释
                    }
                    continue;
                }
                if (nextChar == '*') {
                    while ((c = getNextChar()) != '*') ;
                    c = getNextChar();
                    if (c == '/') {
                        c = getNextChar();
                        continue;
                    }
                    System.out.println("没有匹配的*/");
                    errorNumber++;
                    System.out.println("总错误数为" + errorNumber);
                    close();
                    return;
                }

                // 可能不是注释
                if (nextChar == '=') {
                    writeOutput("(/=,-)\n");
                } else {
                    writeOutput("(/,-)\n");
                    c = nextChar;
                    continue;
                }
            } else if (isOperator(c) >= 0) {
                char nextChar = getNextChar();
                if (isOperator(c) == 0 && nextChar == '=') {
                    String string = "(" + c + nextChar + ",-)\n";
                    writeOutput(string);
                } else if (isOperator(c) == 1 && nextChar == c) {
                    writeOutput("(" + c + c + ",-)\n");
                } else {
                    writeOutput("(" + c + ",-)\n");
                    c = nextChar;
                    continue;
                }
            } else {
                // 其他字符
                System.out.println("第" + lineNumber + "行" + c + "字符违法");
                errorNumber++;
            }
            c = getNextChar();
        }
        System.out.println("总错误数为" + errorNumber);
        printTable();
        close();
    }

    private void printTable() {
        // 打印标识符表
        System.out.println("标识符表");
        for (Identifier<Integer> identifier : identifierMap.values()) {
            System.out.println(identifier.name + " " + identifier.value);
        }

        // 打印常量表
        System.out.println("常量表");
        for (Map.Entry<Double, Integer> entry : constants.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }

    }

    /**
     * 关闭输入输出流
     */
    private void close() {
        try {
            bw.close();
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 写入输出
     *
     * @param string
     */
    private void writeOutput(String string) {
        try {
            bw.write(string);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 倒回指针
     */
    private void fallbackPointer() {
        // 倒回指针
        try {
            br.reset();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private char getNextChar() {
        // 读取下一个字符
        try {
            // 标记当前位置,方便倒回
            br.mark(1);
            return (char) br.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (char) -1;
    }

    private boolean isSpaceOrTab(char c) {
        return c == ' ' || c == '\t';
    }

    /**
     * 判断是否为特殊字符
     *
     * @param c
     * @return
     */
    private boolean isSpecialCharacter(char c) {
        return specialCharacters.contains(c);
    }

    /**
     * 判断是否为操作符
     *
     * @param c
     * @return
     */
    private int isOperator(char c) {
        switch (c) {
            case '+', '-', '*', '/', '%', '<', '>', '=', '!' -> {
                return 0;
            }
            case '&', '|' -> {
                return 1;
            }
            default -> {
                return -1;
            }
        }
    }

    /**
     * 判断是否为字母
     *
     * @param c 字符
     * @return
     */
    private boolean isLetter(char c) {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    }

    /**
     * 判断是否为数字
     *
     * @param c 字符
     * @return
     */
    private boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    /**
     * 初始化词法分析器
     */
    private void init() {
        // 打开文件
        try {
            br = new BufferedReader(new FileReader(inputFileName));
            bw = new BufferedWriter(new FileWriter(outputFileName));
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 初始化关键字集合
        String[] keywordsArray = {"if", "else", "while", "for", "int", "char", "float", "double", "return", "void", "const", "then", "switch", "break"};
        keywords = new HashSet<>(Arrays.stream(keywordsArray).toList());

        // 初始化标识符集合
        identifierMap = new HashMap<>();

        // 初始化常量集合
        constants = new HashMap<>();

        // 初始化特殊字符集合
        Character[] specialCharactersArray = {',', '(', ')', ';', ':', '{', '}', '[', ']'};
        specialCharacters = new HashSet<>(Arrays.stream(specialCharactersArray).toList());
    }

    /**
     * 标识符对象
     */
    private static class Identifier<T> {
        private String name;
        private String type;
        private T value;

        public Identifier(String name, String type, T value) {
            this.name = name;
            this.type = type;
            this.value = value;
        }
    }
}
